Micron Document
🎖️GitЯра🎖️


Displaying Raw • Download

feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt 78274c792318331b11ab64cd28e58e829875b8c2 (78274c79) Text, 12.65 KB

T8b949e/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

Tff7b72package T7ee787org.meshtastic.feature.node.metrics

Tff7b72import T7ee787androidx.compose.foundation.ExperimentalFoundationApi
Tff7b72import T7ee787androidx.compose.foundation.combinedClickable
Tff7b72import T7ee787androidx.compose.foundation.layout.Box
Tff7b72import T7ee787androidx.compose.foundation.layout.PaddingValues
Tff7b72import T7ee787androidx.compose.foundation.layout.Row
Tff7b72import T7ee787androidx.compose.foundation.layout.Spacer
Tff7b72import T7ee787androidx.compose.foundation.layout.fillMaxSize
Tff7b72import T7ee787androidx.compose.foundation.layout.fillMaxWidth
Tff7b72import T7ee787androidx.compose.foundation.layout.heightIn
Tff7b72import T7ee787androidx.compose.foundation.layout.padding
Tff7b72import T7ee787androidx.compose.foundation.layout.width
Tff7b72import T7ee787androidx.compose.foundation.lazy.LazyColumn
Tff7b72import T7ee787androidx.compose.foundation.lazy.items
Tff7b72import T7ee787androidx.compose.foundation.text.selection.SelectionContainer
Tff7b72import T7ee787androidx.compose.material.icons.Icons
Tff7b72import T7ee787androidx.compose.material.icons.filled.Delete
Tff7b72import T7ee787androidx.compose.material.icons.filled.Group
Tff7b72import T7ee787androidx.compose.material.icons.filled.Groups
Tff7b72import T7ee787androidx.compose.material.icons.filled.PersonOff
Tff7b72import T7ee787androidx.compose.material3.Card
Tff7b72import T7ee787androidx.compose.material3.DropdownMenu
Tff7b72import T7ee787androidx.compose.material3.DropdownMenuItem
Tff7b72import T7ee787androidx.compose.material3.Icon
Tff7b72import T7ee787androidx.compose.material3.MaterialTheme
Tff7b72import T7ee787androidx.compose.material3.Scaffold
Tff7b72import T7ee787androidx.compose.material3.Text
Tff7b72import T7ee787androidx.compose.runtime.Composable
Tff7b72import T7ee787androidx.compose.runtime.getValue
Tff7b72import T7ee787androidx.compose.runtime.mutableStateOf
Tff7b72import T7ee787androidx.compose.runtime.remember
Tff7b72import T7ee787androidx.compose.runtime.setValue
Tff7b72import T7ee787androidx.compose.ui.Alignment
Tff7b72import T7ee787androidx.compose.ui.Modifier
Tff7b72import T7ee787androidx.compose.ui.graphics.vector.ImageVector
Tff7b72import T7ee787androidx.compose.ui.res.pluralStringResource
Tff7b72import T7ee787androidx.compose.ui.text.AnnotatedString
Tff7b72import T7ee787androidx.compose.ui.text.SpanStyle
Tff7b72import T7ee787androidx.compose.ui.text.buildAnnotatedString
Tff7b72import T7ee787androidx.compose.ui.text.font.FontWeight
Tff7b72import T7ee787androidx.compose.ui.text.withStyle
Tff7b72import T7ee787androidx.compose.ui.tooling.preview.PreviewLightDark
Tff7b72import T7ee787androidx.compose.ui.unit.dp
Tff7b72import T7ee787androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
Tff7b72import T7ee787androidx.lifecycle.compose.collectAsStateWithLifecycle
Tff7b72import T7ee787org.jetbrains.compose.resources.pluralStringResource
Tff7b72import T7ee787org.jetbrains.compose.resources.stringResource
Tff7b72import T7ee787org.meshtastic.core.model.fullRouteDiscovery
Tff7b72import T7ee787org.meshtastic.core.model.getTracerouteResponse
Tff7b72import T7ee787org.meshtastic.core.strings.Res
Tff7b72import T7ee787org.meshtastic.core.strings.delete
Tff7b72import T7ee787org.meshtastic.core.strings.routing_error_no_response
Tff7b72import T7ee787org.meshtastic.core.strings.traceroute
Tff7b72import T7ee787org.meshtastic.core.strings.traceroute_diff
Tff7b72import T7ee787org.meshtastic.core.strings.traceroute_direct
Tff7b72import T7ee787org.meshtastic.core.strings.traceroute_hops
Tff7b72import T7ee787org.meshtastic.core.ui.component.MainAppBar
Tff7b72import T7ee787org.meshtastic.core.ui.component.SNR_FAIR_THRESHOLD
Tff7b72import T7ee787org.meshtastic.core.ui.component.SNR_GOOD_THRESHOLD
Tff7b72import T7ee787org.meshtastic.core.ui.component.SimpleAlertDialog
Tff7b72import T7ee787org.meshtastic.core.ui.theme.AppTheme
Tff7b72import T7ee787org.meshtastic.core.ui.theme.StatusColors.StatusGreen
Tff7b72import T7ee787org.meshtastic.core.ui.theme.StatusColors.StatusOrange
Tff7b72import T7ee787org.meshtastic.core.ui.theme.StatusColors.StatusYellow
Tff7b72import T7ee787org.meshtastic.feature.node.metrics.CommonCharts.MS_PER_SEC
Tff7b72import T7ee787org.meshtastic.proto.MeshProtos
Tff7b72import T7ee787java.text.DateFormat

Tf0883e@OptInTb4b4b4(Te6edf3ExperimentalFoundationApiTff7b72::Te6edf3classTb4b4b4)
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffLongMethodTa5d6ff"Tb4b4b4)
Tf0883e@Composable
Tff7b72fun Td2a8ffTracerouteLogScreenTb4b4b4(
Te6edf3modifierTb4b4b4: Te6edf3Modifier Tff7b72= Te6edf3ModifierTb4b4b4,
Te6edf3viewModelTb4b4b4: Te6edf3MetricsViewModel Tff7b72= Te6edf3hiltViewModelTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3onNavigateUpTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3state Tff7b72by Te6edf3viewModelTb4b4b4.Te6edf3stateTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3dateFormat Tff7b72= Te6edf3remember Tb4b4b4{ Te6edf3DateFormatTb4b4b4.Te6edf3getDateTimeInstanceTb4b4b4(Te6edf3DateFormatTb4b4b4.Te6edf3SHORTTb4b4b4, Te6edf3DateFormatTb4b4b4.Te6edf3MEDIUMTb4b4b4) Tb4b4b4}

Tff7b72fun Td2a8ffgetUsernameTb4b4b4(Te6edf3nodeNumTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Tffa657String Tff7b72= Te6edf3withTb4b4b4(Te6edf3viewModelTb4b4b4.Te6edf3getUserTb4b4b4(Te6edf3nodeNumTb4b4b4)Tb4b4b4) Tb4b4b4{ Ta5d6ff"Tffd700$Te6edf3longNameTa5d6ff (Tffd700$Te6edf3shortNameTa5d6ff)Ta5d6ff" Tb4b4b4}

Tff7b72var Te6edf3showDialog Tff7b72by Te6edf3remember Tb4b4b4{ Te6edf3mutableStateOfTff7b72<Te6edf3AnnotatedString?Tff7b72>Tb4b4b4(Tff7b72nullTb4b4b4) Tb4b4b4}

Tff7b72if Tb4b4b4(Te6edf3showDialog Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3message Tff7b72= Te6edf3showDialog Tff7b72?: Te6edf3AnnotatedStringTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4) T8b949e// Should not be null if dialog is shown
Te6edf3SimpleAlertDialogTb4b4b4(
Te6edf3title Tff7b72= Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3tracerouteTb4b4b4,
Te6edf3text Tff7b72= Tb4b4b4{ Te6edf3SelectionContainer Tb4b4b4{ Te6edf3TextTb4b4b4(Te6edf3text Tff7b72= Te6edf3messageTb4b4b4) Tb4b4b4} Tb4b4b4}Tb4b4b4,
Te6edf3onDismiss Tff7b72= Tb4b4b4{ Te6edf3showDialog Tff7b72= Tff7b72null Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Te6edf3ScaffoldTb4b4b4(
Te6edf3topBar Tff7b72= Tb4b4b4{
Te6edf3MainAppBarTb4b4b4(
Te6edf3title Tff7b72= Te6edf3stateTb4b4b4.Te6edf3nodeTff7b72?.Te6edf3userTff7b72?.Te6edf3longName Tff7b72?: Ta5d6ff"Ta5d6ff"Tb4b4b4,
Te6edf3ourNode Tff7b72= Tff7b72nullTb4b4b4,
Te6edf3showNodeChip Tff7b72= Tff7b72falseTb4b4b4,
Te6edf3canNavigateUp Tff7b72= Tff7b72trueTb4b4b4,
Te6edf3onNavigateUp Tff7b72= Te6edf3onNavigateUpTb4b4b4,
Te6edf3actions Tff7b72= Tb4b4b4{Tb4b4b4}Tb4b4b4,
Te6edf3onClickChip Tff7b72= Tb4b4b4{Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}Tb4b4b4,
Tb4b4b4) Tb4b4b4{ Te6edf3innerPadding Tff7b72-Tff7b72>
Te6edf3LazyColumnTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3modifierTb4b4b4.Te6edf3fillMaxSizeTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3paddingTb4b4b4(Te6edf3innerPaddingTb4b4b4)Tb4b4b4,
Te6edf3contentPadding Tff7b72= Te6edf3PaddingValuesTb4b4b4(Te6edf3horizontal Tff7b72= T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3itemsTb4b4b4(Te6edf3stateTb4b4b4.Te6edf3tracerouteRequestsTb4b4b4, Te6edf3key Tff7b72= Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3uuid Tb4b4b4}Tb4b4b4) Tb4b4b4{ Te6edf3log Tff7b72-Tff7b72>
Tff7b72val Te6edf3result Tff7b72=
Te6edf3rememberTb4b4b4(Te6edf3stateTb4b4b4.Te6edf3tracerouteRequestsTb4b4b4, Te6edf3logTb4b4b4.Te6edf3fromRadioTb4b4b4.Te6edf3packetTb4b4b4.Te6edf3idTb4b4b4) Tb4b4b4{
Te6edf3stateTb4b4b4.Te6edf3tracerouteResultsTb4b4b4.Te6edf3find Tb4b4b4{
Tffa657itTb4b4b4.Te6edf3fromRadioTb4b4b4.Te6edf3packetTb4b4b4.Te6edf3decodedTb4b4b4.Te6edf3requestId Tff7b72=Tff7b72= Te6edf3logTb4b4b4.Te6edf3fromRadioTb4b4b4.Te6edf3packetTb4b4b4.Te6edf3id
Tb4b4b4}
Tb4b4b4}
Tff7b72val Te6edf3route Tff7b72= Te6edf3rememberTb4b4b4(Te6edf3resultTb4b4b4) Tb4b4b4{ Te6edf3resultTff7b72?.Te6edf3fromRadioTff7b72?.Te6edf3packetTff7b72?.Te6edf3fullRouteDiscovery Tb4b4b4}

Tff7b72val Te6edf3time Tff7b72= Te6edf3dateFormatTb4b4b4.Te6edf3formatTb4b4b4(Te6edf3logTb4b4b4.Te6edf3received_dateTb4b4b4)
Tff7b72val Tb4b4b4(Te6edf3textTb4b4b4, Te6edf3iconTb4b4b4) Tff7b72= Te6edf3routeTb4b4b4.Te6edf3getTextAndIconTb4b4b4(Tb4b4b4)
Tff7b72var Te6edf3expanded Tff7b72by Te6edf3remember Tb4b4b4{ Te6edf3mutableStateOfTb4b4b4(Tff7b72falseTb4b4b4) Tb4b4b4}

Tff7b72val Te6edf3tracerouteDetailsAnnotatedTb4b4b4: Te6edf3AnnotatedString? Tff7b72=
Te6edf3resultTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3res Tff7b72-Tff7b72>
Tff7b72if Tb4b4b4(Te6edf3route Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3routeTb4b4b4.Te6edf3routeListTb4b4b4.Te6edf3isNotEmptyTb4b4b4(Tb4b4b4) Tff7b72&Tff7b72& Te6edf3routeTb4b4b4.Te6edf3routeBackListTb4b4b4.Te6edf3isNotEmptyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3seconds Tff7b72=
Tb4b4b4(Te6edf3resTb4b4b4.Te6edf3received_date Tff7b72- Te6edf3logTb4b4b4.Te6edf3received_dateTb4b4b4)Tb4b4b4.Te6edf3coerceAtLeastTb4b4b4(T79c0ff0Tb4b4b4)Tb4b4b4.Te6edf3toDoubleTb4b4b4(Tb4b4b4) Tff7b72/ Te6edf3MS_PER_SEC
Tff7b72val Te6edf3annotatedBase Tff7b72=
Te6edf3annotateTracerouteTb4b4b4(Te6edf3resTb4b4b4.Te6edf3fromRadioTb4b4b4.Te6edf3packetTb4b4b4.Te6edf3getTracerouteResponseTb4b4b4(Tff7b72::Te6edf3getUsernameTb4b4b4)Tb4b4b4)
Te6edf3buildAnnotatedString Tb4b4b4{
Te6edf3appendTb4b4b4(Te6edf3annotatedBaseTb4b4b4)
Te6edf3appendTb4b4b4(Ta5d6ff"Ta5d6ff\Ta5d6ffnTa5d6ff\Ta5d6ffnDuration: Tffd700${Ta5d6ff"Tff7b72%Tb4b4b4.T79c0ff1fTa5d6ff"Tb4b4b4.Te6edf3formatTb4b4b4(Te6edf3secondsTb4b4b4)Tffd700}Ta5d6ff sTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4} Tff7b72else Tb4b4b4{
T8b949e// For cases where there's a result but no full route, display plain text
Te6edf3resTb4b4b4.Te6edf3fromRadioTb4b4b4.Te6edf3packetTb4b4b4.Te6edf3getTracerouteResponseTb4b4b4(Tff7b72::Te6edf3getUsernameTb4b4b4)Tff7b72?.Te6edf3let Tb4b4b4{ Te6edf3AnnotatedStringTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Te6edf3Box Tb4b4b4{
Te6edf3TracerouteItemTb4b4b4(
Te6edf3icon Tff7b72= Te6edf3iconTb4b4b4,
Te6edf3text Tff7b72= Ta5d6ff"Tffd700$Te6edf3timeTa5d6ff - Tffd700$Te6edf3textTa5d6ff"Tb4b4b4,
Te6edf3modifier Tff7b72=
Te6edf3ModifierTb4b4b4.Te6edf3combinedClickableTb4b4b4(Te6edf3onLongClick Tff7b72= Tb4b4b4{ Te6edf3expanded Tff7b72= Tff7b72true Tb4b4b4}Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3tracerouteDetailsAnnotated Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3showDialog Tff7b72= Te6edf3tracerouteDetailsAnnotated
Tb4b4b4} Tff7b72else Tff7b72if Tb4b4b4(Te6edf3result Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
T8b949e// Fallback for results that couldn't be fully annotated but have basic info
Tff7b72val Te6edf3basicInfo Tff7b72= Te6edf3resultTb4b4b4.Te6edf3fromRadioTb4b4b4.Te6edf3packetTb4b4b4.Te6edf3getTracerouteResponseTb4b4b4(Tff7b72::Te6edf3getUsernameTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3basicInfo Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3showDialog Tff7b72= Te6edf3AnnotatedStringTb4b4b4(Te6edf3basicInfoTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Te6edf3DropdownMenuTb4b4b4(Te6edf3expanded Tff7b72= Te6edf3expandedTb4b4b4, Te6edf3onDismissRequest Tff7b72= Tb4b4b4{ Te6edf3expanded Tff7b72= Tff7b72false Tb4b4b4}Tb4b4b4) Tb4b4b4{
Te6edf3DeleteItem Tb4b4b4{
Te6edf3viewModelTb4b4b4.Te6edf3deleteLogTb4b4b4(Te6edf3logTb4b4b4.Te6edf3uuidTb4b4b4)
Te6edf3expanded Tff7b72= Tff7b72false
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffDeleteItemTb4b4b4(Te6edf3onClickTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4) Tb4b4b4{
Te6edf3DropdownMenuItemTb4b4b4(
Te6edf3onClick Tff7b72= Te6edf3onClickTb4b4b4,
Te6edf3text Tff7b72= Tb4b4b4{
Te6edf3RowTb4b4b4(Te6edf3verticalAlignment Tff7b72= Te6edf3AlignmentTb4b4b4.Te6edf3CenterVerticallyTb4b4b4) Tb4b4b4{
Te6edf3IconTb4b4b4(
Te6edf3imageVector Tff7b72= Te6edf3IconsTb4b4b4.Te6edf3DefaultTb4b4b4.Te6edf3DeleteTb4b4b4,
Te6edf3contentDescription Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3deleteTb4b4b4)Tb4b4b4,
Te6edf3tint Tff7b72= Te6edf3MaterialThemeTb4b4b4.Te6edf3colorSchemeTb4b4b4.Te6edf3errorTb4b4b4,
Tb4b4b4)
Te6edf3SpacerTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3widthTb4b4b4(T79c0ff1T79c0ff2.Te6edf3dpTb4b4b4)Tb4b4b4)
Te6edf3TextTb4b4b4(Te6edf3text Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3deleteTb4b4b4)Tb4b4b4, Te6edf3color Tff7b72= Te6edf3MaterialThemeTb4b4b4.Te6edf3colorSchemeTb4b4b4.Te6edf3errorTb4b4b4)
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffTracerouteItemTb4b4b4(Te6edf3iconTb4b4b4: Te6edf3ImageVectorTb4b4b4, Te6edf3textTb4b4b4: Tffa657StringTb4b4b4, Te6edf3modifierTb4b4b4: Te6edf3Modifier Tff7b72= Te6edf3ModifierTb4b4b4) Tb4b4b4{
Te6edf3CardTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3modifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3heightInTb4b4b4(Te6edf3min Tff7b72= T79c0ff5T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4.Te6edf3paddingTb4b4b4(Te6edf3vertical Tff7b72= T79c0ff2.Te6edf3dpTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3RowTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3paddingTb4b4b4(Te6edf3horizontal Tff7b72= T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4, Te6edf3vertical Tff7b72= T79c0ff8.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3verticalAlignment Tff7b72= Te6edf3AlignmentTb4b4b4.Te6edf3CenterVerticallyTb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3IconTb4b4b4(Te6edf3imageVector Tff7b72= Te6edf3iconTb4b4b4, Te6edf3contentDescription Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3tracerouteTb4b4b4)Tb4b4b4)
Te6edf3SpacerTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3widthTb4b4b4(T79c0ff8.Te6edf3dpTb4b4b4)Tb4b4b4)
Te6edf3TextTb4b4b4(Te6edf3text Tff7b72= Te6edf3textTb4b4b4, Te6edf3style Tff7b72= Te6edf3MaterialThemeTb4b4b4.Te6edf3typographyTb4b4b4.Te6edf3bodyLargeTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

T8b949e/** Generates a display string and icon based on the route discovery information. */
Tf0883e@Composable
Tff7b72private Tff7b72fun Te6edf3MeshProtosTb4b4b4.Td2a8ffRouteDiscoveryTff7b72?.Te6edf3getTextAndIconTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3PairTff7b72<Tffa657StringTb4b4b4, Te6edf3ImageVectorTff7b72> Tff7b72= Tff7b72when Tb4b4b4{
Tff7b72this Tff7b72=Tff7b72= Tff7b72null Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3routing_error_no_responseTb4b4b4) Te6edf3to Te6edf3IconsTb4b4b4.Te6edf3DefaultTb4b4b4.Te6edf3PersonOff
Tb4b4b4}
T8b949e// A direct route means the sender and receiver are the only two nodes in the route.
Te6edf3routeCount Tff7b72<Tff7b72= T79c0ff2 Tff7b72&Tff7b72& Te6edf3routeBackCount Tff7b72<Tff7b72= T79c0ff2 Tff7b72-Tff7b72> Tb4b4b4{ T8b949e// also check routeBackCount for direct to be more robust
Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3traceroute_directTb4b4b4) Te6edf3to Te6edf3IconsTb4b4b4.Te6edf3DefaultTb4b4b4.Te6edf3Group
Tb4b4b4}

Te6edf3routeCount Tff7b72=Tff7b72= Te6edf3routeBackCount Tff7b72-Tff7b72> Tb4b4b4{
Tff7b72val Te6edf3hops Tff7b72= Te6edf3routeCount Tff7b72- T79c0ff2
Te6edf3pluralStringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3pluralsTb4b4b4.Te6edf3traceroute_hopsTb4b4b4, Te6edf3hopsTb4b4b4, Te6edf3hopsTb4b4b4) Te6edf3to Te6edf3IconsTb4b4b4.Te6edf3DefaultTb4b4b4.Te6edf3Groups
Tb4b4b4}

Tff7b72else Tff7b72-Tff7b72> Tb4b4b4{
T8b949e// Asymmetric route
Tff7b72val Te6edf3towards Tff7b72= Te6edf3maxOfTb4b4b4(T79c0ff0Tb4b4b4, Te6edf3routeCount Tff7b72- T79c0ff2Tb4b4b4)
Tff7b72val Te6edf3back Tff7b72= Te6edf3maxOfTb4b4b4(T79c0ff0Tb4b4b4, Te6edf3routeBackCount Tff7b72- T79c0ff2Tb4b4b4)
Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3traceroute_diffTb4b4b4, Te6edf3towardsTb4b4b4, Te6edf3backTb4b4b4) Te6edf3to Te6edf3IconsTb4b4b4.Te6edf3DefaultTb4b4b4.Te6edf3Groups
Tb4b4b4}
Tb4b4b4}

T8b949e/**
* Converts a raw traceroute string into an [AnnotatedString] with SNR values highlighted according to their quality.
*
* @param inString The raw string output from a traceroute response.
* @return An [AnnotatedString] with SNR values styled, or an empty [AnnotatedString] if input is null.
*/
Tf0883e@Composable
Tff7b72fun Td2a8ffannotateTracerouteTb4b4b4(Te6edf3inStringTb4b4b4: Tffa657String?Tb4b4b4)Tb4b4b4: Te6edf3AnnotatedString Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3inString Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Tff7b72return Te6edf3buildAnnotatedString Tb4b4b4{ Te6edf3appendTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4) Tb4b4b4}
Tff7b72return Te6edf3buildAnnotatedString Tb4b4b4{
Te6edf3inStringTb4b4b4.Te6edf3linesTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3forEachIndexed Tb4b4b4{ Te6edf3iTb4b4b4, Te6edf3line Tff7b72-Tff7b72>
Tff7b72if Tb4b4b4(Te6edf3i Tff7b72> T79c0ff0Tb4b4b4) Te6edf3appendTb4b4b4(Ta5d6ff"Ta5d6ff\Ta5d6ffnTa5d6ff"Tb4b4b4)
T8b949e// Example line: "⇊ -8.75 dB SNR"
Tff7b72if Tb4b4b4(Te6edf3lineTb4b4b4.Te6edf3trimStartTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3startsWithTb4b4b4(Ta5d6ff"Ta5d6ff⇊Ta5d6ff"Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3snrRegex Tff7b72= Te6edf3RegexTb4b4b4(Ta5d6ff"""Ta5d6ff⇊ ([Ta5d6ff\Ta5d6ffdTa5d6ff\Ta5d6ff.Ta5d6ff\Ta5d6ff?-]+) dBTa5d6ff"""Tb4b4b4)
Tff7b72val Te6edf3snrMatch Tff7b72= Te6edf3snrRegexTb4b4b4.Te6edf3findTb4b4b4(Te6edf3lineTb4b4b4)
Tff7b72val Te6edf3snrValue Tff7b72= Te6edf3snrMatchTff7b72?.Te6edf3groupValuesTff7b72?.Te6edf3getOrNullTb4b4b4(T79c0ff1Tb4b4b4)Tff7b72?.Te6edf3toFloatOrNullTb4b4b4(Tb4b4b4)

Tff7b72if Tb4b4b4(Te6edf3snrValue Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3snrColor Tff7b72=
Tff7b72when Tb4b4b4{
Te6edf3snrValue Tff7b72>Tff7b72= Te6edf3SNR_GOOD_THRESHOLD Tff7b72-Tff7b72> Te6edf3MaterialThemeTb4b4b4.Te6edf3colorSchemeTb4b4b4.Te6edf3StatusGreen
Te6edf3snrValue Tff7b72>Tff7b72= Te6edf3SNR_FAIR_THRESHOLD Tff7b72-Tff7b72> Te6edf3MaterialThemeTb4b4b4.Te6edf3colorSchemeTb4b4b4.Te6edf3StatusYellow
Tff7b72else Tff7b72-Tff7b72> Te6edf3MaterialThemeTb4b4b4.Te6edf3colorSchemeTb4b4b4.Te6edf3StatusOrange
Tb4b4b4}
Te6edf3withStyleTb4b4b4(Te6edf3style Tff7b72= Te6edf3SpanStyleTb4b4b4(Te6edf3color Tff7b72= Te6edf3snrColorTb4b4b4, Te6edf3fontWeight Tff7b72= Te6edf3FontWeightTb4b4b4.Te6edf3BoldTb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3appendTb4b4b4(Te6edf3lineTb4b4b4) Tb4b4b4}
Tb4b4b4} Tff7b72else Tb4b4b4{
T8b949e// Append line as is if SNR value cannot be parsed
Te6edf3appendTb4b4b4(Te6edf3lineTb4b4b4)
Tb4b4b4}
Tb4b4b4} Tff7b72else Tb4b4b4{
T8b949e// Append non-SNR lines as is
Te6edf3appendTb4b4b4(Te6edf3lineTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tf0883e@PreviewLightDark
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffTracerouteItemPreviewTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3dateFormat Tff7b72= Te6edf3DateFormatTb4b4b4.Te6edf3getDateTimeInstanceTb4b4b4(Te6edf3DateFormatTb4b4b4.Te6edf3SHORTTb4b4b4, Te6edf3DateFormatTb4b4b4.Te6edf3MEDIUMTb4b4b4)
Te6edf3AppTheme Tb4b4b4{
Te6edf3TracerouteItemTb4b4b4(Te6edf3icon Tff7b72= Te6edf3IconsTb4b4b4.Te6edf3DefaultTb4b4b4.Te6edf3GroupTb4b4b4, Te6edf3text Tff7b72= Ta5d6ff"Tffd700${Te6edf3dateFormatTb4b4b4.Te6edf3formatTb4b4b4(Te6edf3SystemTb4b4b4.Te6edf3currentTimeMillisTb4b4b4(Tb4b4b4)Tb4b4b4)Tffd700}Ta5d6ff - DirectTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.08s